package com.huanf.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.huanf.config.MailConfig;
import com.huanf.contant.GptContant;
import com.huanf.domain.ApiKey;
import com.huanf.domain.ChatInfo;
import com.huanf.enums.SystemEnum;
import com.huanf.exception.SystemException;
import com.huanf.handle.SseStreamListener;
import com.huanf.response.ResponseResult;
import com.huanf.service.ApiKeyService;
import com.huanf.service.ChatInfoService;
import com.plexpt.chatgpt.ChatGPT;
import com.plexpt.chatgpt.ChatGPTStream;
import com.plexpt.chatgpt.entity.chat.ChatCompletion;
import com.plexpt.chatgpt.entity.chat.ChatCompletionResponse;
import com.plexpt.chatgpt.entity.chat.Message;
import com.plexpt.chatgpt.listener.ConsoleStreamListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class BaseController {

    @Autowired
    private ApiKeyService apiKeyService;

    @Autowired
    private ChatInfoService chatInfoService;

    @Autowired
    private JavaMailSender mailSender;

    @Autowired
    private MailConfig mailConfig;


    /**
     * 向ChatGPT发送请求的模板，可向任何模型发送请求。流式
     * q 用户的询问文本
     * request 用来记录ip的
     * response 告诉前端我要返回流给你了
     * model 官方提供的模型名称
     */
    protected ChatGPTStream initChatGPTStream(ApiKey apiKey) {
        return ChatGPTStream.builder()
                .apiKey(apiKey.getValue())
                .apiHost(apiKey.getUrl())
                .build()
                .init();
    }
    protected SseEmitter getEmitter(String q, HttpServletRequest request, HttpServletResponse response, String model) {
        checkRateLimit(request);
        SseEmitter sseEmitter = new SseEmitter();
        response.setContentType("text/event-stream");
        response.setCharacterEncoding("UTF-8");

        // 提示词
        Message system = Message.ofSystem("你能够对任何问题进行诙谐有趣的回答，你的回答尽可能精简");
        Message message = Message.of(q);

        ChatCompletion chatCompletion = ChatCompletion.builder()
                .model(model)
                .messages(Arrays.asList(system, message))
                .maxTokens(4096)
                .temperature(0.9)
                .build();
        ChatGPTStream chatGPTStream = initChatGPTStream(getApi());

        // 输出给前端用户
        SseStreamListener sseListener = new SseStreamListener(sseEmitter);
        chatGPTStream.streamChatCompletion(chatCompletion, sseListener);

        // 输出到idea控制台 下面两行可以去掉
        ConsoleStreamListener listener = new ConsoleStreamListener();
        chatGPTStream.streamChatCompletion(chatCompletion, listener);

        return sseEmitter;
    }

    /**
     * 向ChatGPT发送请求的模板，可向任何模型发送请求。非流式
     * apiKey 可用的key
     * q 用户的询问文本
     * chatGPT 官方提供的对象
     * model 官方提供的模型名称
     */
    protected ChatGPT initChatGPT(ApiKey apiKey) {
        return ChatGPT.builder()
                .apiKey(apiKey.getValue())
                .apiHost(apiKey.getUrl())
                .build()
                .init();
    }
    protected ResponseResult getResponseResult(String q, HttpServletRequest request, ChatCompletionResponse response) {
        Message res = response.getChoices().get(0).getMessage();
        String msg = res.getContent();
        record(q, msg, request);
        return ResponseResult.success(msg);
    }
    protected ChatCompletionResponse initChatAnyTemplate(HttpServletRequest request, ApiKey apiKey, String q, ChatGPT chatGPT, String model) {
        checkRateLimit(request);
        Message system = Message.ofSystem("你能够对任何问题进行诙谐有趣的回答，你的回答尽可能精简");//提示词
        Message message = Message.of(q);

        ChatCompletion chatCompletion = ChatCompletion.builder()
                .model(model)
                .messages(Arrays.asList(system, message))
                .maxTokens(4096)
                .temperature(0.9)
                .build();

        try {
            return chatGPT.chatCompletion(chatCompletion);
        } catch (RuntimeException e) {
            String res = e.getMessage();
            //您提供的api-key无权访问该模型
            if (StrUtil.contains(res, "无权")) {
                throw new SystemException(SystemEnum.REFUSE_VISIT_MODEL);
            }
            //官方那边的负载过大
            if (StrUtil.contains(res, "负载")) {
                throw new SystemException(SystemEnum.LOAD_MAX);
            }
            //官方那边的负载过大
            if (StrUtil.contains(res, "渠道")) {
                throw new SystemException(SystemEnum.LOAD_MAX);
            }
            //官方那边检测发现可疑，别请求太快
            if (StrUtil.contains(res, "request")) {
                throw new SystemException(SystemEnum.REQUEST_FAST);
            }
            //纯属自己key的问题，把当前key更新为无效，并且返回一个有效的ApiKey对象
            ApiKey goodKey = removeKey(apiKey.getId());
            ChatGPT result = initChatGPT(goodKey);
            try {
                //新的有效key，但是也不能保证官方那边不报错，所以捕获一下官方那边报出来的异常
                return result.chatCompletion(chatCompletion);
            } catch (RuntimeException r) {
                //官方那边的负载过大
                if (StrUtil.contains(res, "负载")) {
                    throw new SystemException(SystemEnum.LOAD_MAX);
                }
                //官方那边检测发现可疑，别请求太快
                if (StrUtil.contains(res, "request")) {
                    throw new SystemException(SystemEnum.REQUEST_FAST);
                }
                throw new SystemException(SystemEnum.UNKNOWN_ERROR);
            }
        }
    }

    /**
     * 接口限流保护 每分钟最多使用GptContant.MINUTE_REQUEST_MAX次，超出则响应SystemEnum.REQUEST_MAX_SEND错误
     */
    private static final Map<String, RequestCounter> requestCounters = new ConcurrentHashMap<>();
    private static final long MINUTE_REQUEST_MAX = GptContant.MINUTE_REQUEST_MAX;
    private static class RequestCounter {
        int count;
        long lastRequestTime;
    }
    public void checkRateLimit(HttpServletRequest request) {
        String ip = getIp(request);
        long currentTime = System.currentTimeMillis();
        RequestCounter counter = requestCounters.computeIfAbsent(ip, k -> new RequestCounter());
        synchronized (counter) {
            // 清除过期的计数
            if (currentTime - counter.lastRequestTime > 60000) {
                counter.count = 0;
                counter.lastRequestTime = currentTime;
            }
            counter.count++;

            if (counter.count > MINUTE_REQUEST_MAX) {
                throw new SystemException(606, "一分钟之内限制最多使用"+ MINUTE_REQUEST_MAX +"次");
            }
        }
    }


    /**
     * 获取可用的ApiKey对象
     */
    protected ApiKey getApi(){
        LambdaQueryWrapper<ApiKey> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ApiKey::getStatus, GptContant.KEY_AVAILABLE_TRUE);
        List<ApiKey> list;
        list = apiKeyService.list(wrapper);
        if (CollUtil.isEmpty(list)) {
            LambdaQueryWrapper<ApiKey> wrapperNew = new LambdaQueryWrapper<>();
            wrapperNew.eq(ApiKey::getStatus, GptContant.KEY_AVAILABLE_WAIT);
            list = apiKeyService.list(wrapperNew);
        }
        if (CollUtil.isEmpty(list)) {
            throw new SystemException(SystemEnum.FAIL);
        }
        if (!CollUtil.isEmpty(list)){
            return list.get(0);
        }
        throw new SystemException(SystemEnum.FAIL);
    }


    /**
     * 检查出可用的key
     */
    private ApiKey checkKey() {
        LambdaQueryWrapper<ApiKey> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ApiKey::getStatus, GptContant.KEY_AVAILABLE_WAIT); //找未使用的key
        List<ApiKey> list = apiKeyService.list(wrapper);
        ApiKey domain = new ApiKey();
        for (ApiKey key : list) {//找可用的kay
            ChatGPT temp = initChatGPT(key);
            try {
                //找到
                temp.chat("你好");
                BeanUtil.copyProperties(key,domain);
                domain.setStatus(GptContant.KEY_AVAILABLE_TRUE);//把这个新key设置为当前正在使用的key
                apiKeyService.updateById(domain);
                return domain;
            } catch (RuntimeException n) {
                //没找到
                BeanUtil.copyProperties(key,domain);
                domain.setStatus(GptContant.KEY_AVAILABLE_FALSE);//把这个key设置为不可用
                apiKeyService.updateById(domain);
            }
        }
        sendMailCode();//邮件告警
        throw new SystemException(SystemEnum.FAIL);
    }

    /**
     * 处理掉不可用的key，并返回一个可用的ApiKey对象
     */
    protected ApiKey removeKey(Long id) {
        ApiKey execute = new ApiKey();
        apiKeyService.updateById(execute.builder().status(GptContant.KEY_AVAILABLE_FALSE).id(id).build());
        return checkKey();
    }

    /**
     * 获取访问者ip
     */
    public String getIp(HttpServletRequest request){
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (ip != null && ip.length() > 15) {
            if (ip.indexOf(",") > 0) {
                ip = ip.substring(0, ip.indexOf(","));
            }
        }
        return ip;
    }

    /**
     * 获取访问者的地理位置、经纬度
     * https://lbsyun.baidu.com/
     * 把ak替换成你申请的，用我的也行
     */
    protected String getAddress(String ip){
        String url = String.format("https://api.map.baidu.com/location/ip?ip=%s&coor=bd09ll&ak=YpKIAXUMViEqWAaXdul5Wjj26a1pwvs1", ip);
        String result2 = HttpUtil.get(url, CharsetUtil.CHARSET_UTF_8);

        JSONObject jsonObject = JSONUtil.parseObj(result2);
        JSONObject contentObj = jsonObject.getJSONObject("content");
        JSONObject pointObj = contentObj.getJSONObject("point");
        String city = contentObj.getStr("address");//省市
        String longitude = pointObj.getStr("x");//经度
        String latitude = pointObj.getStr("y");//纬度

        String address = city + "-" + "经度(" + longitude + ")" + "-" + "纬度(" + latitude + ")";
        return address;
    }

    /**
     * 获取用户所使用的客户端
     */
    public static String getUserAgent(HttpServletRequest request) {
        return request.getHeader("User-Agent");
    }

    /**
     * 问答记录在数据库
     */
    protected void record(String ask, String reply, HttpServletRequest request) {
        ChatInfo chatInfo = new ChatInfo();
        String ip = getIp(request);
        chatInfo.setAsk(ask);
        chatInfo.setReply(reply);
        if (!StrUtil.hasBlank(ip) && !ip.equals("127.0.0.1") && !ip.equals("0:0:0:0:0:0:0:1")) {
            try {
                chatInfo.setAddress(getAddress(ip));
                chatInfo.setIp(ip);
            } catch (Exception e) {
                throw new SystemException(SystemEnum.FAIL_ADDRESS);
            }
        } else {
            chatInfo.setIp(ip);
            chatInfo.setAddress("无");
        }
        chatInfo.setCt(DateUtil.now());
        String userAgent = getUserAgent(request);
        if (userAgent.length() > 150) {
            userAgent = userAgent.substring(0, 150);
        }
        chatInfo.setClient(userAgent);
        chatInfoService.save(chatInfo);
    }

    /**
     * 发送验证码，默认是不开启的，在application.yml文件中，配置enable属性为true即可开启
     */
    public Boolean sendMailCode(){ //本地测试轻松通过。如果是服务器的话，要放通465端口
        if (mailConfig.getEnable().equals(false)) {
            throw new SystemException(SystemEnum.MAIL_SEND_FAIL);
        }
        QueryWrapper<ApiKey> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status", GptContant.KEY_AVAILABLE_FALSE);
        int badCount = apiKeyService.count(queryWrapper);
        int totalCount = apiKeyService.count();
        String StrContent = "您共有 "+totalCount+" 个key，截至目前有 "+badCount+" 个key已失效，请及时补充至api_key表";
        try{
            SimpleMailMessage message = new SimpleMailMessage();
            //发送人的账号
            message.setFrom(mailConfig.getUsername());
            //接受者的账号
            message.setTo(mailConfig.getTarget());
            //邮件的标题
            message.setSubject(mailConfig.getTitle());
            //邮件的内容
            message.setText(StrContent);
            //邮件的发送时间
            message.setSentDate(DateUtil.parse(DateUtil.now()));
            //开启ssl即可使用465端口 避免云厂商不给你开启25端口
            JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
            Properties properties = new Properties();
            properties.setProperty("mail.smtp.auth", "true");//开启认证
            properties.setProperty("mail.debug", "true");//启用调试
            properties.setProperty("mail.smtp.timeout", "200000");//设置链接超时
            properties.setProperty("mail.smtp.port", Integer.toString(25));//设置端口
            properties.setProperty("mail.smtp.socketFactory.port", Integer.toString(465));//设置ssl端口
            properties.setProperty("mail.smtp.socketFactory.fallback", "false");
            properties.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
            javaMailSender.setJavaMailProperties(properties);
            //发送邮件
            mailSender.send(message);
            return true;
        } catch (Exception e){
            throw new SystemException(SystemEnum.MAIL_PORT_FAIL);
        }
    }

}